package com.yeskery.nut.cloud.loadbanlancer;

import com.yeskery.nut.cloud.feign.RecordStats;
import com.yeskery.nut.cloud.registry.core.Instance;
import com.yeskery.nut.cloud.registry.core.Server;

import java.security.SecureRandom;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

/**
 * 响应时间优先的选择器
 * @author YESKERY
 * 2024/1/19
 */
public class WeightedResponseTimeChooser implements Chooser, Recorder {

    /** 响应时间map */
    private final Map<String, Long> responseTimeMap = new HashMap<>();

    /** 随机数生成器 */
    private final SecureRandom secureRandom = new SecureRandom(String.valueOf(System.currentTimeMillis()).getBytes());

    /** 随机选择器 */
    private final Chooser randomChooser = new RandomChooser();

    @Override
    public Instance choose(Server server) {
        List<Instance> unUsedInstances = server.getInstances().stream()
                .filter(i -> responseTimeMap.keySet().stream().noneMatch(r -> r.equals(i.id())))
                .collect(Collectors.toList());
        if (!unUsedInstances.isEmpty()) {
            return unUsedInstances.get(0);
        }
        List<Map.Entry<String, Long>> entries = responseTimeMap.entrySet()
                .stream()
                .filter(r -> server.getInstances().stream().anyMatch(i -> r.getKey().equals(i.id())))
                .collect(Collectors.toList());
        if (entries.isEmpty()) {
            return randomChooser.choose(server);
        }
        long sum = entries.stream().mapToLong(Map.Entry::getValue).sum();
        Map<String, Range> rangeMap = new HashMap<>(entries.size());
        int total = 0;
        for (Map.Entry<String, Long> entry : entries) {
            Range range = new Range();
            range.start = total;
            range.end = 100 - (int) (entry.getValue() / sum);
            total = range.end + 1;
            if (range.end < 0) {
                range.end = range.start;
            } else if (range.end > 100) {
                range.end = 100;
            }
            rangeMap.put(entry.getKey(), range);
        }
        int value = secureRandom.nextInt(101);
        Optional<Map.Entry<String, Range>> optional = rangeMap.entrySet()
                .stream()
                .filter(r -> r.getValue().start >= value && r.getValue().end <= value)
                .findFirst();
        if (optional.isPresent()) {
            Optional<Instance> serverOptional = server.getInstances()
                    .stream()
                    .filter(r -> r.id().equals(optional.get().getKey()))
                    .findFirst();
            if (serverOptional.isPresent()) {
                return serverOptional.get();
            }
        }
        return randomChooser.choose(server);
    }

    @Override
    public void success(RecordStats recordStats, Instance instance) {
        if (instance == null) {
            return;
        }
        long runTime = recordStats.getEndTime() - recordStats.getStartTime();
        updateResponseTime(instance.id(), runTime);
    }

    @Override
    public void failure(RecordStats recordStats, Instance instance, Throwable throwable) {
        if (instance == null) {
            return;
        }
        Long responseTime = responseTimeMap.get(instance.id());
        if (responseTime == null) {
            responseTimeMap.put(instance.id(), 5000L);
        } else {
            responseTimeMap.put(instance.id(), responseTime * 3);
        }
    }

    /**
     * 更新响应时间
     * @param instanceId 服务实例id
     * @param runTime 运行时间
     */
    private void updateResponseTime(String instanceId, Long runTime) {
        Long result = responseTimeMap.computeIfPresent(instanceId, (k, o) -> (o + runTime) / 2);
        if (result == null) {
            responseTimeMap.put(instanceId, runTime);
        }
    }

    /**
     * 取值范围
     * @author YESKERY
     * 2024/1/19
     */
    private static final class Range {
        /** 开始 */
        private int start;
        /** 结束 */
        private int end;
    }
}
